クラウドの基礎
この部の 2 / 5 章 ・ 全体で 71 / 76 章 ・ 読了目安 40 分
- プロジェクト・IAM・ネットワークの関係を説明できる
- 権限エラーが出た時にどこを見るか分かる
- コストがどこで発生しているか追える
コンテナと Kubernetesで扱った Kubernetes は、何もない空間には浮いていません。 その下には必ず、マシン・ネットワーク・課金の主体があります。それがクラウドです。
この章では GCP を題材にしますが、名前が違うだけで考え方はどのクラウドでも同じです。 新人に求められるのは基盤を設計することではなく、自分の権限がなぜ足りないのか、 そのお金がどこから出ているのかを説明できることです。
なぜクラウドなのか
「サーバーを買わなくていい」は結論の半分
よく言われるのは「機材を買わなくていい」「初期費用がかからない」です。 それは事実ですが、本質はそこではありません。
クラウドの本質は、インフラがAPI で操作できるようになったことです。
# サーバーが1台増える。所要時間30秒
gcloud compute instances create worker-01 --machine-type=e2-medium
# 消える。所要時間30秒
gcloud compute instances delete worker-01これがなぜ効くのか。API で作れるということは、次がすべて可能になるということです。
| できるようになること | 意味 |
|---|---|
| コードで書ける | インフラ構成を Git で管理し、レビューできる(Terraform など) |
| 使い捨てにできる | 検証用の環境を丸ごと作って、終わったら消せる |
| 自動で増やせる | 負荷に応じてスケールする。コンテナと Kubernetesの HPA はこの上に乗っている |
| 同じものを再現できる | dev と prod を同じ定義から作れる |
つまりクラウドとは「安いレンタルサーバー」ではなく、 インフラをソフトウェアとして扱えるようにする仕組みです。
作れるということは、消し忘れられるということ
同じ理由で、事故も起きます。
30秒で作れるものは、30秒で忘れられます。 誰も使っていないのに課金され続けるリソースは、どの現場にも必ずあります。 この章の後半で扱うコストの話は、すべてこの裏返しです。
オンプレミス(自社の機材)では、サーバーを追加するのに稟議と納期が必要でした。 だから「最初に多めに買って、余らせておく」のが正解でした。
クラウドでは逆です。必要になってから足すのが正解で、 先に大きく確保しておくのは、ただの無駄遣いになります。
「余裕をもって大きめのマシンにしておきました」は、 オンプレの発想が抜けていないサインです。
プロジェクト — すべての境界
GCP でリソースを作るときは、必ずどれかのプロジェクトの中に作ります。 プロジェクトは単なるフォルダ分けではなく、3つの境界を同時に持つ単位です。
| 境界 | 意味 |
|---|---|
| 課金 | 請求はプロジェクト単位で集計される。「どこにいくらかかったか」の単位 |
| 権限 | IAM の付与はプロジェクト単位が基本。ここに入れた人は中のリソースを触れる |
| リソース | ネットワーク、VM、GKE クラスタ、DB。すべてどれかのプロジェクトに属する |
組織 (example.com)
└── フォルダ (プロダクトA)
├── プロジェクト myapp-dev ← 開発。壊してよい
├── プロジェクト myapp-staging ← 本番と同じ構成で検証
└── プロジェクト myapp-prod ← 本番。触れる人を絞る
環境はプロジェクトごと分ける
dev / staging / prod を1つのプロジェクトの中で名前だけ変えて共存させる構成は、 小さく始めるときは楽ですが、実務ではまず採りません。理由は3つです。
- 権限を分けられない — 開発者に dev の権限を渡すと、prod も触れてしまう
- コストを分けられない — 請求が混ざり、どの環境が高いのか分からない
- 事故が波及する — 検証中の設定変更やクォータ超過が本番に届く
プロジェクトを分ければ、この3つが設定ではなく構造として分離されます。
コンソールに表示される名前(MyApp Prod)は後から変えられますが、
コマンドや API が使うプロジェクト ID(myapp-prod-4f21)は作成後に変更できません。
そして ID は全世界でユニークなので、myapp-prod のような短い名前は
たいてい他人に取られていて、末尾に乱数が付いた形になります。
結果、myapp-prod-4f21 と myapp-prd-4f21 のような紛らわしい ID が並ぶことになります。
コマンドを打つ前に、今どのプロジェクトを向いているかを必ず確認してください。
IAM — 誰が、何を、どこで
IAM(Identity and Access Management)は、権限の仕組みです。 覚えることは1つだけで、権限は必ず3点セットで決まります。
プリンシパル(誰が) × ロール(何をしてよいか) × リソース(どこで)
この3つのどれか1つでも欠けたり、ズレていたりすると権限エラーになります。 逆に言えば、権限エラーの原因は必ずこの3つのどれかです。
プリンシパル(誰が)
| 種類 | 例 | 用途 |
|---|---|---|
| ユーザー | you@example.com | 人間 |
| グループ | backend-team@example.com | 人間の集合。個人に直接付けず、これに付ける |
| サービスアカウント | api@myapp-prod.iam.gserviceaccount.com | プログラムの身元 |
ロール(何をしてよいか)
| 種類 | 例 | 使うか |
|---|---|---|
| 基本ロール | roles/owner roles/editor roles/viewer | 本番では使わない。粗すぎる |
| 事前定義ロール | roles/storage.objectViewer | これが基本。用途ごとに用意されている |
| カスタムロール | 自分で権限を列挙する | 事前定義で足りない時だけ |
roles/editor は「プロジェクト内のほぼ全部を書き換えられる」権限です。
一見便利ですが、これを配るとIAM を分けている意味がなくなります。
リソース(どこで)と継承
IAM は階層で継承されます。上で付けた権限は下にすべて効きます。
組織 ← ここで付けると全プロジェクトに効く(事故ると影響が大きい)
└ フォルダ
└ プロジェクト ← 実務でよく使う粒度
└ 個別リソース(バケット、Spanner インスタンスなど) ← 最も絞れる
下で拒否しても、上で許可されていれば通ります。 「バケット単位では付けていないのに読めてしまう」時は、たいてい上位に権限が付いています。
サービスアカウントとは何か
プログラム自身のアカウントです。人間のアカウントとは別物として扱います。
人間のアカウント サービスアカウント
you@example.com api@myapp-prod.iam.gserviceaccount.com
パスワード + MFA 鍵、または実行環境に紐づく身元
退職したら消える サービスが動く限り存在する
GKE の Pod や Cloud Run のサービスには、このサービスアカウントが紐づきます。 アプリが Spanner を読めるかどうかは、あなたの権限ではなく、 そのサービスアカウントの権限で決まります。
「自分の権限で動かせば早い」は、次の4つを同時に壊します。
- 退職・異動でサービスが止まる — アカウントが消えた瞬間に本番が動かなくなる
- 監査ログが意味を失う — 「誰がやったか」が全部あなたの名前になり、 人間の操作とプログラムの動作を区別できない
- 権限が過剰になる — 人間には開発用の広い権限が付いていることが多い
- 認証情報が漏れた時の影響が最大化する — あなたが触れる全リソースが巻き添えになる
プログラムの権限はサービスアカウントに、人間の権限は人間のアカウントに。 この分離が、あとから直すのが最も面倒な部分です。最初から分けてください。
最小権限
「とりあえず動かすために roles/editor を付ける」は、実務で最も多い妥協です。
そして一度動いてしまうと、誰も権限を削りに戻りません。
正しい順番はこうです。
# 1. まず必要そうな最小のロールだけ付ける
gcloud projects add-iam-policy-binding myapp-dev \
--member="serviceAccount:api@myapp-dev.iam.gserviceaccount.com" \
--role="roles/spanner.databaseReader"
# 2. 動かす。足りなければエラーメッセージに不足している権限名が出る
# PERMISSION_DENIED: spanner.databases.write
# 3. その権限を含むロールだけを足すエラーメッセージには足りない権限の正確な名前が書いてあります。 それを読んで足すのが、最短かつ最小の道です。
権限エラーが出た時に見る順番
この節が実務で最も使います。 PERMISSION_DENIED や 403 を見たら、上から順に確認します。
# 0. まず、今どのプロジェクトで、誰として実行しているかを確認する
gcloud config list
gcloud auth list # ACTIVE の行が今の身元
# 1. エラーメッセージから「不足している権限名」を読む
# 例) Permission 'storage.objects.get' denied on resource ...
# 2. その身元に、どのロールが付いているかを見る
gcloud projects get-iam-policy myapp-dev \
--flatten="bindings[].members" \
--filter="bindings.members:api@myapp-dev.iam.gserviceaccount.com" \
--format="table(bindings.role)"
# 3. アプリの場合、実際に使われている身元を確認する
# (自分の権限ではなく、サービスアカウントの権限で動いている)
kubectl get serviceaccount api -o yaml # GKE の場合
gcloud run services describe api --format="value(spec.template.spec.serviceAccountName)"チェックリストとしてはこうです。
| # | 確認すること | よくある原因 |
|---|---|---|
| 1 | プロジェクトは合っているか | dev のつもりが prod、あるいはその逆 |
| 2 | 誰として実行しているか | 自分の権限で通っていたが、本番ではサービスアカウントだった |
| 3 | どの権限が足りないか | エラー文字列に権限名が書いてある。読む |
| 4 | ロールは付いているか | そもそも付与漏れ |
| 5 | リソースの階層は合っているか | プロジェクトには付いているが、対象は別プロジェクトのリソース |
| 6 | 反映されているか | IAM の変更は反映に数十秒〜数分かかることがある |
特に6番は見落とされます。IAM の変更は即座に全体へ伝播するとは限らないので、 付与直後に失敗しても1〜2分待ってもう一度試してください。
ここで焦って roles/editor を付けてしまい、そのまま本番に残る——
というのが、権限が過剰になっていく典型的な経路です。
Cloud Run 上のアプリが Spanner の読み取りで PERMISSION_DENIED になりました。あなたの手元では gcloud で同じデータを読めています。最初に確認すべきことは?
ネットワーク
構成要素
| 要素 | 役割 |
|---|---|
| VPC | プロジェクト内のプライベートなネットワーク。この中は互いに通信できる |
| サブネット | VPC をリージョンごとに区切ったもの。IP アドレスの範囲を持つ |
| ファイアウォール | どの通信を通すかのルール。デフォルトは受信拒否 |
| ロードバランサ | 外からのアクセスを受け、複数の宛先へ振り分ける |
| Cloud NAT | 外向き通信だけをまとめて出すための出口 |
インターネット
↓
ロードバランサ(グローバル IP を持つのはここだけ)
↓
┌─────────── VPC ───────────────┐
│ サブネット (asia-northeast1) │
│ GKE ノード / Cloud Run │
│ ↓ 内部 IP のみ │
│ Spanner / Cloud SQL │ ← 外から直接は届かない
└────────────────────────────────┘
なぜ DB にグローバル IP を付けてはいけないか
「開発中に手元から繋ぎたいから」という理由で、
DB にグローバル IP を付けて 0.0.0.0/0 を許可する——これは最も危険な近道です。
理由は「危ないから」では足りません。具体的にはこうなります。
- 公開した瞬間からスキャンされます。 インターネット上の全 IP は常時スキャンされており、 よく使うポート(5432、3306、6379 など)は数分以内に接続を試されます
- 守りがパスワード1枚だけになります。 ネットワークの層が消えるので、 パスワードが漏れた、あるいは弱かった時点で終わりです
- 脆弱性が出た時に即死します。 DB 本体に認証回避の脆弱性が見つかった時、 ネットワークで隔離されていれば猶予がありますが、公開していると猶予がありません
- 気づけません。 攻撃者は壊すのではなく、静かに読んで出ていきます
正しいやり方は、内部 IP のままにして、経路の方を用意することです。
- アプリからは VPC 内の内部 IP で繋ぐ(そもそも外に出さない)
- 開発者が繋ぐ時は 踏み台・プロキシ・IAP トンネルを経由する
- ファイアウォールは送信元を絞る(
0.0.0.0/0は本番では書かない)
# 手元から Cloud SQL に繋ぐ。グローバル IP を付ける必要はない
cloud-sql-proxy myapp-prod:asia-northeast1:main-db
# IAP 経由で踏み台に入る(VM にグローバル IP は不要)
gcloud compute ssh bastion --tunnel-through-iapファイアウォールの送信元に 0.0.0.0/0 と書くのは、
「全世界から接続してよい」と宣言することです。
一時的な検証のつもりで開けたルールは、まず消されません。 「後で閉じます」と言って閉じられた例を、私は見たことがありません。
外部に公開してよいのは、原則としてロードバランサだけです。 それ以外に穴を開けたくなったら、代わりに使える経路(プロキシ・IAP・踏み台)が 本当に無いのかを先に確認してください。
コンピュートの選択肢
「マイクロサービスだから全部 GKE」は、判断を放棄した結果です。 選択肢はそれぞれ運用の重さと引き換えに自由度を得るという関係にあります。
| Cloud Run | GKE | Compute Engine | |
|---|---|---|---|
| 単位 | コンテナ1つ | クラスタ + Pod | 仮想マシン |
| スケール | 自動。ゼロまで落ちる | 自動(設定が必要) | 手動 or MIG |
| 運用の重さ | ほぼ不要 | クラスタとアップグレードの面倒を見る | OS もパッチも自分 |
| 起動の速さ | 数秒(コールドスタートあり) | 常駐 | 分単位 |
| 常時稼働のコスト | リクエストが無ければほぼゼロ | ノードが立っている間ずっと | 起動中ずっと |
| できること | HTTP/gRPC のリクエスト応答が中心 | ほぼ何でも | 何でも |
| 向いている | API、Web、バッチ処理の受け口 | 相互通信する多数のサービス、細かい制御 | 特殊なミドルウェア、レガシー移行 |
判断軸
上から順に問い、最初に「はい」になったところで止まるのが実務的です。
- 既存クラスタに載せる話か? → 既に GKE があるなら GKE。運用は既に払っている
- リクエストに応答するだけか? → Cloud Run。運用コストが桁で違う
- Pod 間の細かい制御、サイドカー、Operator、特殊なスケジューリングが要るか? → GKE
- OS レベルの制御や、コンテナ化できないものがあるか? → Compute Engine
新人ほど「本番は Kubernetes でやるものだ」と思いがちですが、 HTTP/gRPC を受けて返すだけのサービスなら、Cloud Run の方が正解であることは多いです。
GKE を選ぶということは、クラスタのバージョンアップ、ノードプールの管理、 ネットワークポリシー、リソース設計——その全部を継続的に見る約束をするということです。
コンテナと Kubernetesで学んだ内容は、その約束を果たすための知識でした。 知識があることと、常に使うべきことは別です。
マネージドサービスを使うか、自前で立てるか
「Redis なら VM に立てれば安いのでは」——構築だけを見ればその通りです。 コストは構築ではなく、運用に出ます。
| 観点 | 自前で立てる | マネージド |
|---|---|---|
| 構築 | 1日 | 30分 |
| バージョンアップ | 自分で計画・検証・実施 | ボタン or 自動 |
| バックアップ | 仕組みを作り、復元を定期的に試す | 標準機能 |
| 監視 | メトリクスの収集から作る | 最初から付いている |
| 障害対応 | 深夜に自分が起きる | 多くはクラウド側が対応 |
| 可用性 | 冗長構成を自分で設計 | 設定項目 |
| 費用 | 安く見える | 高く見える |
金額だけを比べると自前が勝ちます。しかし比べるべきは、 「そのサービスを3年間動かし続けるのに必要な人の時間」を含めた総額です。
判断の目安はこうです。
- その技術が自社の競争力そのものでないなら、マネージドを使う
- 深夜に起きて対応する覚悟が無いなら、マネージドを使う
- 自前にするのは、マネージドでは要件を満たせないと具体的に説明できる時だけ
インフラの費用比較で自前が安く見えるのは、たいてい人件費を数えていないからです。 エンジニアが月に数時間その面倒を見るだけで、多くのマネージドサービスの差額を上回ります。 「安い」と言うときは、何を数えていないのかを確認してください。
コストはどこで出るか
請求書が跳ねるのは、たいてい計算資源そのものではないところです。
見落とされやすい3つ
| 種類 | 何が起きているか |
|---|---|
| Egress(外向き通信) | クラウドから外へ出るデータに課金される。入る方(Ingress)は無料なことが多い |
| アイドル課金 | 使っていなくても、存在するだけで課金されるもの |
| ログとメトリクスの保存量 | 出した量 × 保存期間で効いてくる |
Egress は特に直感に反します。 リージョンをまたぐ通信、 別プロジェクトへの通信、インターネットへの応答——これらは全部お金です。
同一ゾーン内 → 無料 or ごく安い
リージョン内の別ゾーン → 少し課金
リージョンをまたぐ → 課金
インターネットへ出る → 最も高い(かつ、宛先の地域で単価が違う)
だから「アプリは東京、DB は米国」のような構成は、 遅いだけでなく、通信量に比例して課金され続けます。
存在するだけで課金されるもの
| リソース | 止めても課金されるか |
|---|---|
| VM(停止中) | 本体は止まるが、アタッチされたディスクは課金され続ける |
| 未使用の静的 IP | 使っていない方が高い(使用中は無料のことがある) |
| ロードバランサ | 転送ルールが存在するだけで時間課金 |
| GKE クラスタ | ノードが1台も無くても、クラスタ管理費がかかる場合がある |
| スナップショット・古いイメージ | 消さない限り増え続ける |
新人がやりがちな高額請求
| やりがちなこと | 何が起きるか |
|---|---|
| 検証環境の消し忘れ | GKE クラスタや大きめの VM が動きっぱなし。最も金額が大きい |
| ロードバランサの放置 | 検証で作った転送ルールが残り、毎月定額で溶ける |
debug ログを本番で出す | ログ量が10倍になり、保存料と取り込み料が跳ねる |
| リクエストごとに全件 SELECT | DB の読み取り課金 + Egress の両方が増える |
| 画像や動画を毎回オリジンから配る | CDN を挟まないと Egress が直撃する |
| ループの中の外部 API 呼び出し | 単価は小さくても回数で効く |
読まれるコードを書くで「後から調べるためにログを書く」と学びました。 それは正しいのですが、量には値段が付きます。
debug レベルを本番で出しっぱなしにすると、
取り込み料・保存料の両方が効いて、月の請求で目に見える金額になります。
- 本番のログレベルは
info以上を既定にする - 大量に出るログはサンプリングする(全部の成功リクエストを記録しない)
- 保存期間を決める(多くの調査は直近数日で足りる)
「ログを減らす」ではなく、必要なログを、必要な期間だけ残すが答えです。
自分で追えるようにする
# ラベルを付けておくと、請求を分解できる
gcloud compute instances create worker-01 \
--labels=env=dev,team=backend,service=apiリソースにはラベルを付けます。 付けていないと、請求書を見ても「この2万円は誰のものか」が永久に分かりません。 予算アラートも合わせて設定し、閾値を超えたら気づける状態にしておきます。
本番を壊さないための作法
まず read-only で見る
いきなり変更を打たない。これだけで事故の大半は防げます。
# 見る(安全)
gcloud compute instances list
gcloud run services describe api
kubectl get deployment api -o yaml
# 変える(ここからは慎重に)
gcloud compute instances delete worker-01--dry-run と差分確認
多くのツールには「実行せずに、何が起きるかだけ見る」手段があります。
kubectl apply -f deploy.yaml --dry-run=server # サーバー側で検証だけする
kubectl diff -f deploy.yaml # 今の状態との差分を見る
terraform plan # 適用前に変更内容を確認するplan や diff の出力を読まずに apply するのは、目をつぶって運転するのと同じです。
「削除される予定のリソース」が混ざっていないかは、必ず確認してください。
環境の取り違えを構造で防ぐ
新人の事故で最も多いのがプロジェクトの切り替え忘れです。 「dev で試すつもりが prod だった」は、注意力では防げません。仕組みで防ぎます。
# 名前付きの設定を環境ごとに作る
gcloud config configurations create dev
gcloud config set project myapp-dev
gcloud config configurations create prod
gcloud config set project myapp-prod
# 切り替え
gcloud config configurations activate dev
# 今どこを向いているかを常に確認
gcloud config list
kubectl config current-contextさらに効くのは次の2つです。
- シェルのプロンプトに現在のプロジェクト / コンテキストを表示する(見えていれば間違えない)
- 危険なコマンドでは
--projectを明示する(設定に依存させない)
# 設定がどうなっていようと、対象が一意に決まる
gcloud compute instances delete worker-01 --project=myapp-devそして本番のプロジェクトに入った時は、最初に打つのを必ず確認系にする。
gcloud config list と gcloud auth list の2つです。
これは儀式ではありません。手が覚えている操作ほど、環境を確認せずに実行してしまうからです。
kubectl delete pod を dev の感覚で prod に打った事故は、どの現場にもあります。
実務の落とし穴まとめ
roles/editorで通す — 一度動くと誰も権限を削りに戻らない- 人間のアカウントでアプリを動かす — 退職で止まり、監査ログが意味を失う
- アプリの権限を自分の権限で確かめる — 実際に動くのはサービスアカウント
- DB にグローバル IP を付ける — 数分でスキャンされ、守りがパスワード1枚になる
0.0.0.0/0の一時ルール — 一時的なつもりのルールは閉じられない- 検証用の GKE クラスタとロードバランサの放置 — 請求が跳ねる代表格
- 本番で
debugログ — 取り込み料と保存料の両方が効く - プロジェクトの切り替え忘れ — 注意力ではなく
gcloud configとプロンプト表示で防ぐ
まとめ
- クラウドの本質は安さではなく、インフラを API とコードで扱えること
- プロジェクトは課金・権限・リソースの境界。環境はプロジェクトごと分ける
- IAM は プリンシパル × ロール × リソース。権限エラーの原因は必ずこの3つのどれか
- プログラムはサービスアカウントで動く。人間のアカウントで本番を触らない
- 権限エラーは プロジェクト → 実行者 → 不足権限名 → ロール → 階層 → 反映待ち の順で見る
- 外に出してよいのは原則ロードバランサだけ。DB は内部 IP のまま、経路を用意する
- コンピュートは運用の重さと自由度のトレードオフ。「全部 GKE」は判断ではない
- 自前構築のコストは構築ではなく運用に出る。人の時間を勘定に入れる
- 請求が跳ねるのは Egress・アイドル課金・ログ量。ラベルと予算アラートで追える状態にする
- 本番では read-only で見る →
plan/diffを読む →--projectを明示する
公式ドキュメント
迷ったら一次情報に戻ってください。
| 対象 | リンク |
|---|---|
| Google Cloud ドキュメント | https://cloud.google.com/docs?hl=ja |
| Google Cloud アーキテクチャセンター | https://cloud.google.com/architecture?hl=ja |
| AWS ドキュメント | https://docs.aws.amazon.com/ja_jp/ |
| AWS Well-Architected フレームワーク | https://aws.amazon.com/jp/architecture/well-architected/ |
章末問題
検証のために Cloud SQL にグローバル IP を付け、ファイアウォールで 0.0.0.0/0 からの接続を許可しました。最も深刻な問題はどれですか。
次の章では、ここまで見た構成要素をコードとして管理する方法を扱います。 画面をクリックして作ったインフラは、再現も追跡もできません。