IPアドレス管理(IPAM)で、ネットワークと機器の全体像を把握する
最近、IPアドレス範囲の重複が原因でネットワーク、特にピアリングに支障をきたしているというお客様の声をよく耳にします。これは、組織全体でIPアドレスを計画・管理する仕組みが必要になっているサインです。
共有スプレッドシートでIPを管理する方法もありますが、専用ソフトウェアという選択肢もあります。本記事では、定番のオープンソースIPアドレス管理(IPAM)ツールであるNetboxを、Google Cloud Platform(GCP)上でクラウドネイティブに動かす方法を紹介します。
従来の構成
これまでNetboxは、Webサーバーをフロントに据えた1台または複数台の仮想マシン上で運用されてきました。コミュニティが管理するDockerイメージもありますが、提供されている手順はdocker composeでの実行のみです。とはいえ、このアーキテクチャは多くの企業が構築・運用しているアプリケーションと共通点が多く、パブリッククラウドへの移行例を示す題材としても最適です。

出典:Netbox — 標準的なNetboxインストール構成
クラウドネイティブな設計
そこで、Dockerイメージの仕組み、依存関係、設定パラメータを読み解き、マネージドサービスのみを使ってGCPにデプロイする方法を試してみました。この事例は、パブリッククラウドへの移行において、アプリケーションを「移行しつつ改善する(move and improve)」のか「刷新する(rip and replace)」のかを考えるうえで参考になるはずです。

マネージドサービスを使ってGCP上で再構築したNetboxの構成
アプリケーションの構成要素
- Netboxアプリケーション(Djangoフレームワークを使ったPythonアプリ)
- PostgreSQLデータベース(Cloud SQL)
- Redis(Cloud Memorystore)
設計上のポイント
- マネージド型のデータベースとキャッシュ(Cloud SQL、Cloud Memorystore)
- データベースとキャッシュへの接続はプライベートIPに限定(Private Service Access)
- データベースのホスト名にはプライベートDNSを使用(Cloud DNS)
- シークレットはSecret Managerで管理
- サーバーレスのコンテナランタイム(Cloud Run、Artifact Registry)
- TLS対応のグローバルロードバランサー(HTTP(S) Load Balancing、マネージド証明書)
- WAFファイアウォール(Cloud Armor)
多くの組織がつまずくのが、マネージドサービスやサーバーレスアプリをプライベートIPアドレスで接続する部分です。本例では、VPCネットワークでプライベートレンジを予約し、それをマネージドサービスに割り当てて接続のブリッジを作る手順を紹介します。
Cloud DNSは、アプリがデータベースに接続する際のプライベートホスト名を提供する目的で利用します。これにより、将来データベースを差し替えたりフェイルオーバーが必要になった場合でも、DNSレコードを書き換えるだけでアプリは同じドメインを参照し続けられるため、運用の柔軟性が高まります。理論上はDNSフォワーディングを使ってパブリックドメインに統合することもできますが、内部用途では不要なのでexample.comを使いました。
本来は踏み台(ジャンプホスト)VMは不要ですが、構築中の接続テスト用に1台立てています。通常、踏み台はサイズ1のマネージドインスタンスグループ(MIG)としてデプロイし、外部IPアドレスは付与しない構成にします。
安全でロードバランシングされたWebアプリケーション

Cloud Runサービスをフロントに据えたGlobal Load Balancer経由で配信されるホスト型Netboxアプリケーション
各要素の連携を分かりやすく示すため、個人ドメインを利用し、Global Load Balancerに割り当てた静的IPアドレスに対して「Aレコード」を登録しました。マネージド証明書は自動的にプロビジョニングされます。

セキュリティを高めるため、ロードバランサーにCloud Armor(WAFファイアウォール)ポリシーを適用し、IPレンジの制限もかけています(後述)。
実装コード
以下のコードでは、ネットワーク、環境変数とシークレット、データベース、Artifact RegistryとDockerイメージ、Cloud Run、ロードバランシング、WAFファイアウォールに至るまで、構築に使ったコマンドを順を追って示しています。
さらなる複雑さと考慮点
クラウドネイティブな形でアプリケーションを近代化・デプロイする例としてNetboxを選んだ理由のひとつは、その技術的な複雑さにあります。このアプリにはファイルシステム、セッション、ワーカーに加え、日次のcronによるクリーンアップ処理まで含まれています。

Netboxアプリケーション用のDocker Composeスニペット(netbox-workerとnetbox-housekeepingに注目)
上記のdocker-compose.yamlのスニペットでは、docker-compose固有ではなくYAMLの機能であるアンカーが使われています。

YAMLの機能(アンカー)とマージキーの例
設定を簡潔に複製したうえで、コマンドをオーバーライドして実行時に異なるスクリプトを呼び出せます。
Cloud Runでワーカーを再現する
Cloud Runでこの種の機能を再現するには、--cmdと--argsというフラグが使えます。メインのアプリケーションをデプロイする際のコマンドをそのまま流用し、名前を変えたうえでCMDをオーバーライドし、以下のように別のエントリーポイントスクリプトを実行します。
gcloud run deploy $WORKER_NAME \
--platform managed \
--allow-unauthenticated \
--vpc-connector $CONNECTOR_NAME \
--ingress=internal-and-cloud-load-balancing \
--region $GCP_REGION \
--image $IMAGE_PATH \
--set-env-vars "ALLOWED_HOSTS=$ALLOWED_HOSTS" \
...
--cmd "/opt/netbox/venv/bin/python" \
--args "/opt/netbox/netbox/manage.py" \
--args "rqworker"
Cloud RunとCloud Schedulerによる日次ハウスキーピングジョブ
日次のハウスキーピングジョブは、Cloud Run上に複製したサービスを用意し、Cloud Schedulerから毎日呼び出すスケジュールを設定することで実行できます。
gcloud run deploy $HOUSEKEEPING_NAME \
--platform managed \
--allow-unauthenticated \
--vpc-connector $CONNECTOR_NAME \
--ingress=internal-and-cloud-load-balancing \
--region $GCP_REGION \
--image $IMAGE_PATH \
--set-env-vars "ALLOWED_HOSTS=$ALLOWED_HOSTS" \
...
--cmd "/opt/netbox/housekeeping.sh"
ハウスキーピングサービスをデプロイしたら、Cloud Scheduler APIを有効化します。
gcloud services enable cloudscheduler.googleapis.com
次に、サービスアカウントを作成してinvoker権限を付与し、スケジュールジョブを作成します。
# fetch the service URL
export SVC_URL=$(gcloud run services describe $HOUSEKEEPING_NAME \
--platform managed --region $GCP_REGION --format="value(status.url)")
#########################################################
# create cloud scheduler job
#########################################################
export SA_NAME="cloud-scheduler-runner"
export SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
# create service account
gcloud iam service-accounts create $SA_NAME \
--display-name "${SA_NAME}"
# add sa binding to cloud run app
gcloud run services add-iam-policy-binding $HOUSEKEEPING_NAME \
--platform managed \
--region $GCP_REGION \
--member=serviceAccount:$SA_EMAIL \
--role=roles/run.invoker
# create the job to invoke service every day at 2:30 AM
gcloud scheduler jobs create http housekeeping-job --schedule "30 2 * * *" \
--http-method=GET \
--uri=$SVC_URL \
--oidc-service-account-email=$SA_EMAIL \
--oidc-token-audience=$SVC_URL
ファイルシステム
今回のねらいは、Netboxのような複雑なアプリケーションでもコンポーネントを切り出し、Cloud Runをはじめとするマネージドサービスでクラウドにデプロイできることを示すことでした。このアプリにとってベストな解とは限りませんが、技術的には十分に実現可能です。
ファイルシステムを利用したい場合、現状ではサーバーレスプラットフォームに制約があります。代わりにKubernetes Engine、もしくはシンプルにCompute Engine VMで動かす方法も検討するとよいでしょう。VMをコンテナとして実行することもでき、必要に応じてディスクやボリュームをアタッチできるので非常に便利です。
ただし、Cloud Runで簡易的な「ファイルシステム」を実現する一つの工夫として、サンプルコードや下記スニペットのようにSecret Managerを活用する方法があります。
# create secret for all vars
gcloud secrets create $SECRET_ID --replication-policy="automatic"
gcloud secrets versions add $SECRET_ID --data-file=${PWD}/$SECRET_FILE
# mount file path in cloud run
gcloud run deploy $SERVICE --image $IMAGE_URL \
--update-secrets="/env/netbox.env"=$SECRET_ID:$SECRET_VERSION
ベストプラクティス:サービスアカウントを分ける
本記事の例ではCloud Schedulerのアドオン用に別サービスアカウントを示しましたが、ベストプラクティスは、サービスごと、そして踏み台(VM)ごとに個別のサービスアカウントを用意し、それぞれに必要最小限のIAMロールだけを付与することです。これは最小権限の原則に沿った考え方です。
Cloud Runサービス用には、別途「netbox-runner」サービスアカウントを作成し、必要なロールに絞って付与すべきです。例:
本記事の例を通じて、既存アプリケーションを近代化し、パブリッククラウドのマネージドサービスを活用するイメージをつかんでいただけたなら幸いです。とにかくNetboxを動かしたいだけであれば、上記のコードスニペットで十分ですが、VMやK8Sでの運用も選択肢として検討してみてください。
また、Terraformerのようなサードパーティ製ツールや、GCP公式の一括エクスポート機能を使えば、既存のインフラをリバースエンジニアリングしてTerraformコードを生成し、本記事の構成をTerraform化することも可能です。
ネットワーク構成でIP競合に同じような悩みを抱えているなら、共有スプレッドシートでもNetboxのような定番ツールでも構いません。IPAMの実践に踏み出すタイミングかもしれません。