フューチャー技術ブログ

初めてのGCPで環境構築してハマったこと

はじめに

お仕事でGCP使って環境を構築することがあったのですが、色々とハマることが多かったので供養を兼ねて共有したいと思います。

当時の私の経験値としては「AWSの一部サービスは触ったことがある」程度でクラウド環境を下地から構築するのは初めての経験でした。一度触ってみれば常識だよねって内容が多いですが、初心者が小石につまずいてもすぐに立ち上れるようになれば幸いです。

今回構築した環境の概要

  • 既存のオンプレ環境との共存を前提とし、使えるアドレス範囲もオンプレのNWから払い出し
  • オンプレ環境とインターネットVPNでつなぐプロジェクトは1つ(ホストプロジェクト)
  • 各環境(production、staging・・)は共有VPCで接続(サービスプロジェクト)

なお、構築はTerraform, Ansibleで行いました。

GCPで環境構築してハマったこと

本編です。
カテゴリ別に記載しています。

1. GKE関連

1-1. GKEのコア数を増やせない

原因

アカウントあたりの割り当て上限に達していた。

対応

制限変更を依頼することで解消しました。
自分が依頼したときは数分で対応してくれましたが、常に数分で対応されるかは定かではありません。
サービスインする前は想定されるスケール具合に合わせて事前に拡張しておきましょう。
割り当て増加の手順は他の方が書かれていますのでこちらをご参考に。
https://qiita.com/mouse2/items/dd136453798804f99de7
https://cloud.google.com/compute/quotas?hl=ja&_ga=1.181298212.2042940000.1483498197

1-2. プライベートGKEクラスタからインターネットアクセスできない

原因

プライベートクラスタではノード(pod)に外部IPが付与されないため。

対応

CloudNATを設定しNAT経由でインターネットに出るようにしました。
今回はシステム要件で外部IPの付与が不可でしたが、外部IPが付与されていれば問題なくインターネットアクセスできます。

1-3. GKEへのデプロイ時、kubectlコマンドが接続できない

原因

デプロイサーバが外部IPしか持っていなかったため。
GKEクラスタはこの時点でプライベートクラスタであった。

対応

システム要件もありデプロイサーバに内部IPを付与し、外部IPを削除しました。
その上でGKEクラスタの承認済みネットワークにデプロイサーバの属するサブネットを追加しました。

1-4. GKEのLBでフロントのIPが固定できない

事象

LB作成時にフロントエンドのIPが自動的に割り振られてしまう。

対応

Ingressを利用して意図したIPを割り当てました。
詳細は他の方が書かれていますのでこちらをご参考に。
https://qiita.com/tinjyuu/items/fd7a97b0b81963dcc7f2

1-5. 共有VPC上のGKEクラスタのセカンダリCIDR設計

事象

オンプレ環境と接続する1つの共有VPC上にproduction, stagingなど複数プロジェクトを相乗りさせる場合、各プロジェクトで利用するセカンダリCIDRの設計が必要。

対応

GKEで必要なセカンダリCIDRを本腰入れて設計しました。
スタンダードなこれと言った解はなく、必要な環境数、オンプレ環境から割り当てられたIP範囲から適宜設計する必要があります。
ここだけでも1記事くらいのボリュームになりそうなので詳細はまた別途。

2019/10/17 公開しました:

1-6. GKEクラスタが大量に作成できない

原因

利用可能なセカンダリCIDRの上限に掛かるため。
共有VPC上にプライベートGKEクラスタを構築するためには、1クラスタあたりに2つのアドレス範囲(pod/service)を割り当てる必要があるが、1サブネット内にセカンダリCIDRは 上限5つまで しか作成できない。

今回、1プロジェクト=1サブネットを共有するという方針であったため、1プロジェクト当たりに2つしかクラスタを作成できなかった。

(2019/08/28追記)上限が30までに緩和されたようです。

必要に応じて、サブネットごとに最大 30 個のセカンダリ CIDR ブロックを定義できます。このセカンダリ IP 範囲は、エイリアス IP アドレスにのみ使用できます。この上限を引き上げることはできません。
https://cloud.google.com/vpc/docs/quota#per_network

@urasokoさん、ご指摘ありがとうございます!

これで、GKEのクラスタ数を増やしたい時も対応できます。

対応

用途に応じてクラスタを分けるのではなく、ノードプールを分ける方針としました。
GKEの設計でクラスタを分けるかノードプールを分けるかはしばしば話に挙がりますが、共有VPC上に構築する場合はノードプールを分けるしかないです。

2. CloudSQL関連

2-1. CloudSQLのインスタンスが再作成できない

原因

同じインスタンス名は数日間(1週間ほど)は作成できない仕様であったため。

対応

初期構築時は連番やタイムスタンプなどを入れ確実に構築可能なことの保証が取れてから真名で構築することにしました。
インスタンス名にタイムスタンプなどを入れてクライアント側の接続情報を変更していく運用も考えましたが、担当した案件では開発に複数社入っていたりする状況もあり諦めました。

2-2. CloudSQLのアクセス制限

事象

CloudSQLにアクセス可能なインスタンスを同一プロジェクトのインスタンスに絞りたいが、以下の制約がある。

  • Firewall Ruleはサブネット単位(今回の構成のプロジェクト単位)で指定できない
  • CloudSQLにFirewall Ruleで利用するネットワークタグを設定できない。
  • 承認済みネットワークにプライベートIPは設定できない

対応

出来上がったCloudSQLのIPを、gcloudで取得して、それをFirewall Ruleに設定しました。なおFirewall Ruleはインスタンスに対して適応されるため、インスタンスからの下りに対して制御をかけることしかできません。

デフォルトは下りが全てallowされているので、まずはCloudSQLのIP範囲にdenyをかけて、その上で接続するSQLインスタンスのみallowするという対応をしています。
Firewall Ruleの話だけで1記事くらいのボリュームになりそうなので詳細はまた別途書きます。

2-3. CloudSQL のパスワードが有効では無かった

原因

Terraformでデプロイしていたが、その定義が不正だったため。
後述のサンプルだとインスタンス名が正しく紐づかない。GCPは関係なくTerraformの記述ミスが原因でした。

対応

依存関係があるリソースは、ハードコードで名称を設定しない。
${xxxxx}を使用して実際に設定されている名称を取得する。

// インスタンスの作成
resource "google_sql_database_instance" "db-instance-test" {
name = "test-instance"
/*省略*/
}

// パスワード設定:NGパターン
resource "google_sql_user" "test-postgres-user" {
name = "postgres"
instance = "test-instance"
password = "postgres"
}

// パスワード設定:OKパターン
resource "google_sql_user" "test-postgres-user" {
name = "postgres"
instance = "${google_sql_database_instance.db-instance-test.name}"
password = "postgres"
}

2-4. CloudSqlProxyを使ってSQLインスタンスにつながらない

原因

Cloud SQL Administration APIが有効化されていないため。
CloudSqlProxyを使う際は有効化する必要があります。
https://cloud.google.com/sql/docs/postgres/connect-external-app?hl=ja

対応

Cloud SQL Administration APIを有効化します。
環境の断面が増えてくると、Administration APIの有効化手順が漏れるので、これもちゃんとTerraformで管理することになりました。
(※それまでは手動で有効にしていました☠)

2-5. CloudSQLの接続CIDRが任意のアドレス範囲で作成されてしまった

原因

IPアドレスの設計前にCloudSQLのインスタンスを立てて検証していたため初期構築時にCIDRの割り当てを明確に行っていなかった。

対応

GCPのSREチーム(米国)へ変更対応を依頼しました(ちょうどクリスマスシーズンだったので大変でした)。2019年1月時点では、このCIDRは一度設定するとGUIやコマンドで変更できないため依頼する必要がありました。
共有VPCの環境でCloudSQLのアドレス範囲を明示的に定めたい場合は注意しましょう。

(2019/08/28追記) 2019年4月以降は gcloud services vpc-peerings update コマンドで更新できるそうです。
https://issuetracker.google.com/issues/118849070

これからは、わざわざサポートに連絡を取る必要はありません! 間違っても比較的気軽にやり直せるようになったのは嬉しいですね。

2-6. GCEからCloudSQLへ接続ができない

原因

GCEインスタンスには内部IPのみで外部IPが付与されておらず、SQLインスタンスには外部IPのみで内部IPを付与していなかった(β版のため)。
SQL Proxyが上手いことやってくれると淡い期待をしたがダメであった。

対応

他のシステム要件からCloudSQLに内部IPを付与しました

3. LB関連

3-1. Internal 特定の通信においてInternal LBで負荷分散されない

原因

コネクションを貼って通信していたため。
Internal LBでセッションアフィニティをOFFにしてもコネクション貼って通信するものはコネクション貼ったバックエンドに流れる仕様であった。

※参考
https://cloud.google.com/load-balancing/docs/backend-service?hl=ja&_ga=2.47555939.-570213192.1545967516
https://cloud.google.com/compute/docs/reference/rest/v1/backendServices?hl=ja

対応

クライアント側で定期的にコネクションを貼りなおすようにした。
大量のクライアントがいる処理であれば正常時はコネクションを貼る時点で後ろのノードは分散されれるので問題ないですが、ノード障害で別ノードとコネクション貼ってしまうとノード復旧時に再度振り分けられなくなってしまうのを回避するためです。

3-2. Internal LBのヘルスチェックが通らない

原因

Internal LBのヘルスチェックが「130.211.0.0/22」と「35.191.0.0/16」 内のアドレスから送信されるため。
これらの接続を許可するようにFirewall Ruleを設定する必要があった。

対応

ヘルスチェックの通信元のIPに対して通信を許可するようにファイアウォールルールを追加しました。

4. GCPプロジェクト

4-1. 新規に環境構築できない

原因

課金プロジェクトの作成数に限りがあったため

対応

サポートに連絡して上限を上げてもらった。
コア数の上限同様に事前に確認すべきでした。

4-2. プロジェクトID/名を指定してリソースにアクセスできない。

原因

プロジェクトID=プロジェクト名を前提としてリソースにアクセスしていたが、プロジェクトIDとプロジェクト名が異なっていた。

対応

プロジェクトIDとプロジェクト名を同じに変更しました。
特に制約が無ければ同じにしておいた方がハマりどころ減ります。

4-3. 突然CloudSQLやGKEにアクセスできなくなった。

原因

アカウントが有効なクレジットと紐付いていなかった(!!!)。
最初は無料利用枠で動いてしまうため、構築して数日後に発覚した。

対応

アカウントを有効なクレジットと紐付けた。
プロジェクト構築時のオペレーションとして手順化した。

5. 踏み台

5-1. 踏み台サーバにsshで接続できない

原因

IAMの権限不足。
「編集者」をベースとしたカスタム権限を作成していたが、GCEのログイン権限が継承されていなかった。

対応

IAMに「編集者」を直接付与する形式に変更。
アクセスするメンバーが限られていたため、カスタム権限で細かく制御は不要と判断しました。

5-2. どの環境の踏み台サーバで作業しているか分からない

原因

踏み台サーバのインスタンス名が全ての環境(productionとかstagingとか)で同一のため。
CloudShellだとURL部分にPJ名が出るので判断可能であるがsshのターミナルツールによっては判別できない。

対応

踏み台にはインスタンス名に環境名を付与した。
命名規約上他のインスタンスに対して環境名を付けないとしても、実際にログインして作業することが多い踏み台だけは付けてあげた方が優しいです。

6. その他

6-1. 共有VPC設定ができない

原因

共有VPCを設定するためには「共有 VPC 管理者」権限が必要であったが権限がなかった。
https://cloud.google.com/vpc/docs/provisioning-shared-vpc#nominating_shared_vpc_admins_for_the_organization
更には「共有 VPC 管理者」を付与するために「組織管理者」権限が必要であるが、構築メンバーが誰も権限を持っていなかった。

対応

「組織管理者」権限は契約者しか権限持っていなかったため依頼して操作ユーザに対して「組織管理者」権限を付与し、その上で「共有 VPC 管理者」を付与しました。

6-2. 特定アプリケーションのインストールができない

原因

外部サイトへアクティベーションが必要であったが外部IPが無く接続できなかったため。

対応

一時的に外部IPを付与してインストールを行った。
※前述のCloudNAT導入前だったためこのような対応しています。
※CloudNAT導入後であれば問題なく出来たと思われます。

まとめ

ここまでお疲れ様でした。
忙しい人向けに、全体通してのまとめです。

  • 全体的にネットワーク、権限周りがハマりどころ多かった。
  • GKEは共有VPC、ネットワークの制約によるハマりどころが多かった。
  • 逆にこの辺りがすんなりいけば簡単に環境作れる感じがした。
  • GCP、ネットワーク知識いらないって聞いてた(自分だけ?)けど結構必要。
  • オンプレ環境と密な構成は難易度が跳ね上がる。