こんにちは、分析基盤を担当しながらインフラも面倒見ている鍵本です。
本日は Cloud SQL に private IP を付与して失敗 した話をします。
背景
Zealsは、LINEおよびFacebook Messenger上で動作するチャットボットサービスで、WebのForm体験をチャット上で完結させることができます。この Zeals のアーキテクチャは『進化し続ける「Zeals」サービスを支えるアーキテクチャについて』でご紹介しているとおりで、データベースサーバとして Cloud SQL を利用しております。
具体的には、アプリケーションからは Cloud SQL Proxy を通して接続しています。Zeals がサービスとして成長するにあたり、アプリケーションが動いている GKE の Pod から Cloud SQL へのレイテンシーを低減した方がいいのではないかという話になり、2018年12月6日に GA リリースされた private IP 接続を検討することになりました。実際、@yukinagase さんの GKEからCloud SQLに接続する2つの方法のレイテンシを調べてみた によれば、private IP 接続の方が public IP 接続(Cloud SQL Proxy経由)よりも 7 倍近く早くなっているようです。
必ず事前にやっておくべきこと
Cloud SQL を新規作成あるいは稼働中の設定変更時にプライベートIPのチェックボックスにチェックを入れて保存すると Cloud SQL 用のプライベートサービス接続用のサブネットが自動的に割り当てられますが、これを 自身で予め作成しておくべき です。公式ドキュメント プライベートIP の図に記載されている Allocated range の部分が予め作成しておくサブネットとなります。
作成方法は以下の通りです。
- VPCネットワークの管理画面を開きます
- Cloud SQL を関連付けさせる VPC ネットワークを選択します
- プライベートサービス接続タブを開きます
- IP範囲の割り当て ボタンをクリックします
- 名前とカスタムでIP範囲を入力して割り当てボタンをクリックします
VPCネットワーク cluster-vpc-network-4690d7c に 10.187.160.0/20 を割り当てた時の管理画面が下図のようになります。
ここで注意すべきことは割り当てるIP範囲に 172.17.0.0/16 の範囲にあるサブネットを指定してはいけないということです。なぜ割り当ててはいけないかについては公式ドキュメント プライベート IP 接続の構成 に記載されております。
なお Cloud SQL にプライベートIPを一度設定すると元に戻せない(プライベートIPを外すことができない)のでご注意下さい。
実際に確認した事例
弊社では上記のプライベートサービス接続用のサブネットを予め割り当てることをせず、自動割り当てで設定 してしまいました。ステージング環境等で試して問題がなかったので本番でもえいやっとやってしまったのが間違いのもとでした。
そして割り当てられたサブネットがなんと 172.17.112.0/20!!まさか割り当ててはいけないものに設定されているなんて。そのようなものを引き当てる確率は、private IP として割り当て可能な範囲のうち 172.16.0.0/12 の範囲だけで考えると 1/16(6%)になります。サブネット等で用いられてない割り当て可能な 10.0.0.0/8 や 192.168.0.0/16 の一部も含めれば、スマホゲームの SSR を引き当てるよりも低い確率 です。
再起動後は Cloud SQL Proxy 経由で接続している限りサービスには影響しなかったのですが、フェイルオーバーレプリカとの接続ができなく なっていました。Stackdriver Logging を見ると以下のようなエラーが出ておりました。
2020-02-10T10:12:09.428312Z 8 [ERROR] Slave I/O for channel '': error connecting to master 'cloudsqlreplica@172.17.112.2:3306' - retry-time: 60 retries: 1, Error_code: 2003
つまりレプリケーションが止まっているので、なんらかの障害が発生してマスターサーバがダウンした場合、新しいデータが存在しない状態でフェイルオーバーしてしまう危険な状態に陥ってしまったというわけです。
対処方法
サービスを一時的に停止できる深夜に以下の方法で対処しました。
- 仮のVPCネットワークを作成する
- 仮の VPC ネットワークにプライベートサービス接続のためのIPアドレス範囲を割り当てる
- サービスを停止する
- Cloud SQL を上記 VPC ネットワークに接続し直す
- サービスを再開し状態確認をする
- 元の VPC ネットワークにあったプライベートサービス接続のためのIPアドレス範囲を開放する
- 改めてプライベートサービス接続のためのIPアドレス範囲(172.21.112.0/20)を割り当てる
- サービスを再度停止する
- Cloud SQL を元の VPC ネットワークに接続し直す
- サービスを再開し状態確認をする
- フェイルオーバーレプリカを作成する
11 で作成するフェイルオーバーレプリカの名前は元々の名前を使うことができませんので別の名前を付ける必要があります。GCP ではインスタンスを削除してから 7 日間は同じ名前が使用できない ようになっているようです。
上記の 4, 9 の作業では Cloud SQL が再起動しますので、もし再起動を伴うデータベースフラグの修正を行いたい場合には、9 のタイミングで実施するといいでしょう。我々も innodb_read_io_threads, innodb_write_io_threads の値をついでに変更しました。
まとめ
今回は Cloud SQL に private IP を付与してしまったことで起きた事象の後始末についてのお話をご紹介しました。公式ドキュメントを隅々まで読んでいればこういうことは起きなかっただろうと反省しています。とはいえ上記の対処方法を検討する上でいろいろと学びが多かったなと思います。
最後に
Zeals では、発見した課題をワクワクしながら技術で解決するエンジニアを求めています!ご興味がある方は、ぜひ下記募集からご応募ください!